/* packet-wow.c
 * Routines for World of Warcraft (WoW) protocol dissection
 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/* This dissector is based on the MaNGOS project's source code, Stanford's
 * SRP protocol documents (http://srp.stanford.edu) and RFC 2945: "The SRP
 * Authentication and Key Exchange System." */

#include "config.h"

#include "packet-tcp.h"
#include <epan/charsets.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/wmem_scopes.h>
#include <ptvcursor.h>

void proto_register_wow(void);
void proto_reg_handoff_wow(void);

static dissector_handle_t wow_handle;

typedef enum {
	CMD_AUTH_LOGON_CHALLENGE       = 0x00,
	CMD_AUTH_LOGON_PROOF           = 0x01,
	CMD_AUTH_RECONNECT_CHALLENGE   = 0x02,
	CMD_AUTH_RECONNECT_PROOF       = 0x03,
    CMD_SURVEY_RESULT              = 0x04,
	CMD_REALM_LIST                 = 0x10,
	CMD_XFER_INITIATE              = 0x30,
	CMD_XFER_DATA                  = 0x31,
	CMD_XFER_ACCEPT                = 0x32,
	CMD_XFER_RESUME                = 0x33,
	CMD_XFER_CANCEL                = 0x34
} auth_cmd_e;

static const value_string cmd_vs[] = {
	{ CMD_AUTH_LOGON_CHALLENGE,       "CMD_AUTH_LOGON_CHALLENGE" },
	{ CMD_AUTH_LOGON_PROOF,           "CMD_AUTH_LOGON_PROOF" },
	{ CMD_AUTH_RECONNECT_CHALLENGE,   "CMD_AUTH_RECONNECT_CHALLENGE" },
	{ CMD_AUTH_RECONNECT_PROOF,       "CMD_AUTH_RECONNECT_PROOF" },
	{ CMD_REALM_LIST,                 "CMD_REALM_LIST" },
	{ CMD_XFER_INITIATE,              "CMD_XFER_INITIATE" },
	{ CMD_XFER_DATA,                  "CMD_XFER_DATA" },
	{ CMD_XFER_ACCEPT,                "CMD_XFER_ACCEPT" },
	{ CMD_XFER_RESUME,                "CMD_XFER_RESUME" },
	{ CMD_XFER_CANCEL,                "CMD_XFER_CANCEL" },
	{ 0, NULL }
};

/* AUTOGENERATED_START_ENUM */
typedef enum {
    REALM_TYPE_PLAYER_VS_ENVIRONMENT = 0x0,
    REALM_TYPE_PLAYER_VS_PLAYER = 0x1,
    REALM_TYPE_ROLEPLAYING = 0x6,
    REALM_TYPE_ROLEPLAYING_PLAYER_VS_PLAYER = 0x8,
} e_realm_type;
static const value_string e_realm_type_strings[] =  {
    { REALM_TYPE_PLAYER_VS_ENVIRONMENT, "Player Vs Environment" },
    { REALM_TYPE_PLAYER_VS_PLAYER, "Player Vs Player" },
    { REALM_TYPE_ROLEPLAYING, "Roleplaying" },
    { REALM_TYPE_ROLEPLAYING_PLAYER_VS_PLAYER, "Roleplaying Player Vs Player" },
    { 0, NULL }
};

typedef enum {
    REALM_CATEGORY_DEFAULT = 0x0,
    REALM_CATEGORY_ONE = 0x1,
    REALM_CATEGORY_TWO = 0x2,
    REALM_CATEGORY_THREE = 0x3,
    REALM_CATEGORY_FIVE = 0x5,
} e_realm_category;
static const value_string e_realm_category_strings[] =  {
    { REALM_CATEGORY_DEFAULT, "Default" },
    { REALM_CATEGORY_ONE, "One" },
    { REALM_CATEGORY_TWO, "Two" },
    { REALM_CATEGORY_THREE, "Three" },
    { REALM_CATEGORY_FIVE, "Five" },
    { 0, NULL }
};

typedef enum {
    PROTOCOL_VERSION_TWO = 0x2,
    PROTOCOL_VERSION_THREE = 0x3,
    PROTOCOL_VERSION_FIVE = 0x5,
    PROTOCOL_VERSION_SIX = 0x6,
    PROTOCOL_VERSION_SEVEN = 0x7,
    PROTOCOL_VERSION_EIGHT = 0x8,
} e_protocol_version;
static const value_string e_protocol_version_strings[] =  {
    { PROTOCOL_VERSION_TWO, "Two" },
    { PROTOCOL_VERSION_THREE, "Three" },
    { PROTOCOL_VERSION_FIVE, "Five" },
    { PROTOCOL_VERSION_SIX, "Six" },
    { PROTOCOL_VERSION_SEVEN, "Seven" },
    { PROTOCOL_VERSION_EIGHT, "Eight" },
    { 0, NULL }
};

typedef enum {
    PLATFORM_X86 = 0x783836,
    PLATFORM_POWER_PC = 0x505043,
} e_platform;
static const value_string e_platform_strings[] =  {
    { PLATFORM_X86, "X86" },
    { PLATFORM_POWER_PC, "Power Pc" },
    { 0, NULL }
};

typedef enum {
    OS_WINDOWS = 0x57696E,
    OS_MAC_OS_X = 0x4F5358,
} e_os;
static const value_string e_os_strings[] =  {
    { OS_WINDOWS, "Windows" },
    { OS_MAC_OS_X, "Mac Os X" },
    { 0, NULL }
};

typedef enum {
    LOCALE_EN_GB = 0x656E4742,
    LOCALE_EN_US = 0x656E5553,
    LOCALE_ES_MX = 0x65734D58,
    LOCALE_PT_BR = 0x70744252,
    LOCALE_FR_FR = 0x66724652,
    LOCALE_DE_DE = 0x64654445,
    LOCALE_ES_ES = 0x65734553,
    LOCALE_PT_PT = 0x70745054,
    LOCALE_IT_IT = 0x69744954,
    LOCALE_RU_RU = 0x72755255,
    LOCALE_KO_KR = 0x6B6F4B52,
    LOCALE_ZH_TW = 0x7A685457,
    LOCALE_EN_TW = 0x656E5457,
    LOCALE_EN_CN = 0x656E434E,
} e_locale;
static const value_string e_locale_strings[] =  {
    { LOCALE_EN_GB, "En Gb" },
    { LOCALE_EN_US, "En Us" },
    { LOCALE_ES_MX, "Es Mx" },
    { LOCALE_PT_BR, "Pt Br" },
    { LOCALE_FR_FR, "Fr Fr" },
    { LOCALE_DE_DE, "De De" },
    { LOCALE_ES_ES, "Es Es" },
    { LOCALE_PT_PT, "Pt Pt" },
    { LOCALE_IT_IT, "It It" },
    { LOCALE_RU_RU, "Ru Ru" },
    { LOCALE_KO_KR, "Ko Kr" },
    { LOCALE_ZH_TW, "Zh Tw" },
    { LOCALE_EN_TW, "En Tw" },
    { LOCALE_EN_CN, "En Cn" },
    { 0, NULL }
};

typedef enum {
    LOGIN_RESULT_SUCCESS = 0x00,
    LOGIN_RESULT_FAIL_UNKNOWN0 = 0x01,
    LOGIN_RESULT_FAIL_UNKNOWN1 = 0x02,
    LOGIN_RESULT_FAIL_BANNED = 0x03,
    LOGIN_RESULT_FAIL_UNKNOWN_ACCOUNT = 0x04,
    LOGIN_RESULT_FAIL_INCORRECT_PASSWORD = 0x05,
    LOGIN_RESULT_FAIL_ALREADY_ONLINE = 0x06,
    LOGIN_RESULT_FAIL_NO_TIME = 0x07,
    LOGIN_RESULT_FAIL_DB_BUSY = 0x08,
    LOGIN_RESULT_FAIL_VERSION_INVALID = 0x09,
    LOGIN_RESULT_LOGIN_DOWNLOAD_FILE = 0x0A,
    LOGIN_RESULT_FAIL_INVALID_SERVER = 0x0B,
    LOGIN_RESULT_FAIL_SUSPENDED = 0x0C,
    LOGIN_RESULT_FAIL_NO_ACCESS = 0x0D,
    LOGIN_RESULT_SUCCESS_SURVEY = 0x0E,
    LOGIN_RESULT_FAIL_PARENTALCONTROL = 0x0F,
    LOGIN_RESULT_FAIL_LOCKED_ENFORCED = 0x10,
} e_login_result;
static const value_string e_login_result_strings[] =  {
    { LOGIN_RESULT_SUCCESS, "Success" },
    { LOGIN_RESULT_FAIL_UNKNOWN0, "Fail Unknown0" },
    { LOGIN_RESULT_FAIL_UNKNOWN1, "Fail Unknown1" },
    { LOGIN_RESULT_FAIL_BANNED, "Fail Banned" },
    { LOGIN_RESULT_FAIL_UNKNOWN_ACCOUNT, "Fail Unknown Account" },
    { LOGIN_RESULT_FAIL_INCORRECT_PASSWORD, "Fail Incorrect Password" },
    { LOGIN_RESULT_FAIL_ALREADY_ONLINE, "Fail Already Online" },
    { LOGIN_RESULT_FAIL_NO_TIME, "Fail No Time" },
    { LOGIN_RESULT_FAIL_DB_BUSY, "Fail Db Busy" },
    { LOGIN_RESULT_FAIL_VERSION_INVALID, "Fail Version Invalid" },
    { LOGIN_RESULT_LOGIN_DOWNLOAD_FILE, "Login Download File" },
    { LOGIN_RESULT_FAIL_INVALID_SERVER, "Fail Invalid Server" },
    { LOGIN_RESULT_FAIL_SUSPENDED, "Fail Suspended" },
    { LOGIN_RESULT_FAIL_NO_ACCESS, "Fail No Access" },
    { LOGIN_RESULT_SUCCESS_SURVEY, "Success Survey" },
    { LOGIN_RESULT_FAIL_PARENTALCONTROL, "Fail Parentalcontrol" },
    { LOGIN_RESULT_FAIL_LOCKED_ENFORCED, "Fail Locked Enforced" },
    { 0, NULL }
};


typedef enum {
    REALM_FLAG_NONE = 0x00,
    REALM_FLAG_INVALID = 0x01,
    REALM_FLAG_OFFLINE = 0x02,
    REALM_FLAG_SPECIFY_BUILD = 0x04,
    REALM_FLAG_FORCE_BLUE_RECOMMENDED = 0x20,
    REALM_FLAG_FORCE_GREEN_RECOMMENDED = 0x40,
    REALM_FLAG_FORCE_RED_FULL = 0x80,
} e_realm_flag;

typedef enum {
    SECURITY_FLAG_NONE = 0x0,
    SECURITY_FLAG_PIN = 0x1,
    SECURITY_FLAG_MATRIX_CARD = 0x2,
    SECURITY_FLAG_AUTHENTICATOR = 0x4,
} e_security_flag;

typedef enum {
    ACCOUNT_FLAG_GM = 0x000001,
    ACCOUNT_FLAG_TRIAL = 0x000008,
    ACCOUNT_FLAG_PROPASS = 0x800000,
} e_account_flag;

/* AUTOGENERATED_END_ENUM */

#define WOW_PORT 3724

#define WOW_CLIENT_TO_SERVER pinfo->destport == WOW_PORT
#define WOW_SERVER_TO_CLIENT pinfo->srcport  == WOW_PORT

/* Initialize the protocol and registered fields */
static int proto_wow;


/* More than 1 packet */
static int hf_wow_command;
static int hf_wow_string_length;
/* AUTOGENERATED_START_HF */
static int hf_wow_account_flag;
static int hf_wow_account_name;
static int hf_wow_address;
static int hf_wow_authenticator;
static int hf_wow_build;
static int hf_wow_cd_key_proof;
static int hf_wow_challenge_count;
static int hf_wow_challenge_data;
static int hf_wow_checksum_salt;
static int hf_wow_client_checksum;
static int hf_wow_client_ip_address;
static int hf_wow_client_proof;
static int hf_wow_client_public_key;
static int hf_wow_compressed_data_length;
static int hf_wow_crc_hash;
static int hf_wow_crc_salt;
static int hf_wow_data;
static int hf_wow_decompressed_size;
static int hf_wow_digit_count;
static int hf_wow_error;
static int hf_wow_file_md;
static int hf_wow_file_size;
static int hf_wow_filename;
static int hf_wow_footer_padding;
static int hf_wow_game_name;
static int hf_wow_generator;
static int hf_wow_generator_length;
static int hf_wow_hardware_survey_id;
static int hf_wow_header_padding;
static int hf_wow_height;
static int hf_wow_key_count;
static int hf_wow_large_safe_prime;
static int hf_wow_large_safe_prime_length;
static int hf_wow_locale;
static int hf_wow_locked;
static int hf_wow_login_result;
static int hf_wow_major;
static int hf_wow_matrix_card_proof;
static int hf_wow_minor;
static int hf_wow_name;
static int hf_wow_number_of_characters_on_realm;
static int hf_wow_number_of_realms;
static int hf_wow_number_of_telemetry_keys;
static int hf_wow_offset;
static int hf_wow_os;
static int hf_wow_padding;
static int hf_wow_patch;
static int hf_wow_pin_grid_seed;
static int hf_wow_pin_hash;
static int hf_wow_pin_salt;
static int hf_wow_platform;
static int hf_wow_population;
static int hf_wow_proof_data;
static int hf_wow_protocol_version;
static int hf_wow_protocol_version_int;
static int hf_wow_realm_category;
static int hf_wow_realm_flag;
static int hf_wow_realm_id;
static int hf_wow_realm_type;
static int hf_wow_required;
static int hf_wow_salt;
static int hf_wow_security_flag;
static int hf_wow_seed;
static int hf_wow_server_proof;
static int hf_wow_server_public_key;
static int hf_wow_size;
static int hf_wow_survey_id;
static int hf_wow_unknown_bytes;
static int hf_wow_unknown_int;
static int hf_wow_utc_timezone_offset;
static int hf_wow_width;
/* AUTOGENERATED_END_HF */

static bool wow_preference_desegment = true;

static int ett_wow;
static int ett_message;

struct game_version {
	int8_t major_version;
	int8_t minor_version;
	int8_t patch_version;
	int16_t revision;
};

static int32_t
get_null_terminated_string_length( tvbuff_t* tvb, int32_t offset)
{
    const int32_t maximum_length = 0xFF;
    for (int32_t length = 0; length < maximum_length; length++) {
        uint8_t character = tvb_get_uint8(tvb, offset + length);
        if (character == 0) {
            // Include the null character in the length
            return length + 1;
        }
    }

    return 0;
}

static void
add_cstring(ptvcursor_t* ptv, const int* hf) {
    int32_t len = get_null_terminated_string_length(ptvcursor_tvbuff(ptv), ptvcursor_current_offset(ptv));
    ptvcursor_add(ptv, *hf, len, ENC_UTF_8);
}

static void
add_string(ptvcursor_t* ptv, const int* hf) {
    int32_t len = 0;
    ptvcursor_add_ret_uint(ptv, hf_wow_string_length, 1, ENC_NA, &len);
    ptvcursor_add(ptv, *hf, len, ENC_UTF_8);
}

static unsigned
get_wow_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_)
{
	int8_t size_field_offset = -1;
	uint8_t cmd;
	uint16_t pkt_len;

	cmd = tvb_get_uint8(tvb, offset);

	if(WOW_SERVER_TO_CLIENT && cmd == CMD_REALM_LIST)
		size_field_offset = 1;
	if(WOW_CLIENT_TO_SERVER && cmd == CMD_AUTH_LOGON_CHALLENGE)
		size_field_offset = 2;

	pkt_len = tvb_get_letohs(tvb, size_field_offset);

	return pkt_len + size_field_offset + 2;
}

static void
add_body_fields(packet_info *pinfo, uint8_t header_opcode, ptvcursor_t *ptv, uint32_t *protocol_version) {
    /* AUTOGENERATED_START_VARIABLES */
    uint32_t compressed_data_length = 0;
    uint32_t flag = 0;
    uint32_t generator_length = 0;
    uint32_t large_safe_prime_length = 0;
    uint32_t number_of_realms = 0;
    uint32_t number_of_telemetry_keys = 0;
    uint32_t result = 0;
    uint32_t security_flag = 0;
    uint32_t size = 0;
/* AUTOGENERATED_END_VARIABLES */

    /* AUTOGENERATED_START_PARSER */
    switch (header_opcode) {
        case CMD_AUTH_LOGON_CHALLENGE:
            switch (*protocol_version) {
                case 2:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_protocol_version_int, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_public_key, 32, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_generator_length, 1, ENC_LITTLE_ENDIAN, &generator_length);
                            ptvcursor_add(ptv, hf_wow_generator, generator_length, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_large_safe_prime_length, 1, ENC_LITTLE_ENDIAN, &large_safe_prime_length);
                            ptvcursor_add(ptv, hf_wow_large_safe_prime, large_safe_prime_length, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_salt, 32, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_crc_salt, 16, ENC_NA);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
                case 3:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_protocol_version_int, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_public_key, 32, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_generator_length, 1, ENC_LITTLE_ENDIAN, &generator_length);
                            ptvcursor_add(ptv, hf_wow_generator, generator_length, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_large_safe_prime_length, 1, ENC_LITTLE_ENDIAN, &large_safe_prime_length);
                            ptvcursor_add(ptv, hf_wow_large_safe_prime, large_safe_prime_length, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_salt, 32, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_crc_salt, 16, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_security_flag, 1, ENC_LITTLE_ENDIAN, &security_flag);
                            if (security_flag == SECURITY_FLAG_PIN) {
                                ptvcursor_add(ptv, hf_wow_pin_grid_seed, 4, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_pin_salt, 16, ENC_NA);
                            }
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
                case 5:
                case 6:
                case 7:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_protocol_version_int, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_public_key, 32, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_generator_length, 1, ENC_LITTLE_ENDIAN, &generator_length);
                            ptvcursor_add(ptv, hf_wow_generator, generator_length, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_large_safe_prime_length, 1, ENC_LITTLE_ENDIAN, &large_safe_prime_length);
                            ptvcursor_add(ptv, hf_wow_large_safe_prime, large_safe_prime_length, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_salt, 32, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_crc_salt, 16, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_security_flag, 1, ENC_LITTLE_ENDIAN, &security_flag);
                            if (security_flag & SECURITY_FLAG_PIN) {
                                ptvcursor_add(ptv, hf_wow_pin_grid_seed, 4, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_pin_salt, 16, ENC_NA);
                            }
                            if (security_flag & SECURITY_FLAG_MATRIX_CARD) {
                                ptvcursor_add(ptv, hf_wow_width, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_height, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_digit_count, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_challenge_count, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_seed, 8, ENC_LITTLE_ENDIAN);
                            }
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
                case 8:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_protocol_version_int, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_public_key, 32, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_generator_length, 1, ENC_LITTLE_ENDIAN, &generator_length);
                            ptvcursor_add(ptv, hf_wow_generator, generator_length, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_large_safe_prime_length, 1, ENC_LITTLE_ENDIAN, &large_safe_prime_length);
                            ptvcursor_add(ptv, hf_wow_large_safe_prime, large_safe_prime_length, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_salt, 32, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_crc_salt, 16, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_security_flag, 1, ENC_LITTLE_ENDIAN, &security_flag);
                            if (security_flag & SECURITY_FLAG_PIN) {
                                ptvcursor_add(ptv, hf_wow_pin_grid_seed, 4, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_pin_salt, 16, ENC_NA);
                            }
                            if (security_flag & SECURITY_FLAG_MATRIX_CARD) {
                                ptvcursor_add(ptv, hf_wow_width, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_height, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_digit_count, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_challenge_count, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_seed, 8, ENC_LITTLE_ENDIAN);
                            }
                            if (security_flag & SECURITY_FLAG_AUTHENTICATOR) {
                                ptvcursor_add(ptv, hf_wow_required, 1, ENC_LITTLE_ENDIAN);
                            }
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
            }
            break;
        case CMD_AUTH_LOGON_PROOF:
            switch (*protocol_version) {
                case 2:
                case 3:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_proof, 20, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_hardware_survey_id, 4, ENC_LITTLE_ENDIAN);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_client_public_key, 32, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_crc_hash, 20, ENC_NA);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_telemetry_keys, 1, ENC_LITTLE_ENDIAN, &number_of_telemetry_keys);
                        for (uint32_t i1 = 0; i1 < number_of_telemetry_keys; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "TelemetryKey %i", i1);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 2, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_bytes, 4, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_cd_key_proof, 20, ENC_NA);
                            ptvcursor_pop_subtree(ptv);
                        }
                    }
                break;
                case 5:
                case 6:
                case 7:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_proof, 20, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_hardware_survey_id, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 2, ENC_LITTLE_ENDIAN);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_client_public_key, 32, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_crc_hash, 20, ENC_NA);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_telemetry_keys, 1, ENC_LITTLE_ENDIAN, &number_of_telemetry_keys);
                        for (uint32_t i1 = 0; i1 < number_of_telemetry_keys; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "TelemetryKey %i", i1);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 2, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_bytes, 4, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_cd_key_proof, 20, ENC_NA);
                            ptvcursor_pop_subtree(ptv);
                        }
                    }
                break;
                case 8:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_server_proof, 20, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_account_flag, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_hardware_survey_id, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 2, ENC_LITTLE_ENDIAN);
                        }
                        else {
                            ptvcursor_add(ptv, hf_wow_padding, 2, ENC_LITTLE_ENDIAN);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_client_public_key, 32, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_crc_hash, 20, ENC_NA);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_telemetry_keys, 1, ENC_LITTLE_ENDIAN, &number_of_telemetry_keys);
                        for (uint32_t i1 = 0; i1 < number_of_telemetry_keys; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "TelemetryKey %i", i1);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 2, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_int, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_unknown_bytes, 4, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_cd_key_proof, 20, ENC_NA);
                            ptvcursor_pop_subtree(ptv);
                        }
                    }
                break;
            }
            break;
        case CMD_AUTH_RECONNECT_CHALLENGE:
            switch (*protocol_version) {
                case 2:
                case 5:
                case 6:
                case 7:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_challenge_data, 16, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_checksum_salt, 16, ENC_NA);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
                case 8:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add_ret_uint(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN, &result);
                        if (result == LOGIN_RESULT_SUCCESS) {
                            ptvcursor_add(ptv, hf_wow_challenge_data, 16, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_checksum_salt, 16, ENC_NA);
                        }
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_protocol_version, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_game_name, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                        ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_pop_subtree(ptv);
                        ptvcursor_add(ptv, hf_wow_platform, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_os, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_locale, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_utc_timezone_offset, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_client_ip_address, 4, ENC_LITTLE_ENDIAN);
                        add_string(ptv, &hf_wow_account_name);
                    }
                break;
            }
            break;
        case CMD_AUTH_RECONNECT_PROOF:
            switch (*protocol_version) {
                case 2:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_proof_data, 16, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_checksum, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_key_count, 1, ENC_LITTLE_ENDIAN);
                    }
                break;
                case 5:
                case 6:
                case 7:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_proof_data, 16, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_checksum, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_key_count, 1, ENC_LITTLE_ENDIAN);
                    }
                break;
                case 8:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_login_result, 1, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_proof_data, 16, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_proof, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_client_checksum, 20, ENC_NA);
                        ptvcursor_add(ptv, hf_wow_key_count, 1, ENC_LITTLE_ENDIAN);
                    }
                break;
            }
            break;
        case CMD_REALM_LIST:
            switch (*protocol_version) {
                case 2:
                case 3:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_header_padding, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_realms, 1, ENC_LITTLE_ENDIAN, &number_of_realms);
                        for (uint32_t i1 = 0; i1 < number_of_realms; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Realm %i", i1);
                            ptvcursor_add(ptv, hf_wow_realm_type, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_flag, 1, ENC_LITTLE_ENDIAN);
                            add_cstring(ptv, &hf_wow_name);
                            add_cstring(ptv, &hf_wow_address);
                            ptvcursor_add(ptv, hf_wow_population, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_number_of_characters_on_realm, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_category, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_id, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_pop_subtree(ptv);
                        }
                        ptvcursor_add(ptv, hf_wow_footer_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_padding, 4, ENC_LITTLE_ENDIAN);
                    }
                break;
                case 5:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_header_padding, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_realms, 1, ENC_LITTLE_ENDIAN, &number_of_realms);
                        for (uint32_t i1 = 0; i1 < number_of_realms; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Realm %i", i1);
                            ptvcursor_add(ptv, hf_wow_realm_type, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_locked, 1, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_realm_flag, 1, ENC_LITTLE_ENDIAN);
                            add_cstring(ptv, &hf_wow_name);
                            add_cstring(ptv, &hf_wow_address);
                            ptvcursor_add(ptv, hf_wow_population, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_number_of_characters_on_realm, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_category, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_id, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_pop_subtree(ptv);
                        }
                        ptvcursor_add(ptv, hf_wow_footer_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_padding, 4, ENC_LITTLE_ENDIAN);
                    }
                break;
                case 6:
                case 7:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_header_padding, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_realms, 2, ENC_LITTLE_ENDIAN, &number_of_realms);
                        for (uint32_t i1 = 0; i1 < number_of_realms; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Realm %i", i1);
                            ptvcursor_add(ptv, hf_wow_realm_type, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_locked, 1, ENC_NA);
                            ptvcursor_add(ptv, hf_wow_realm_flag, 1, ENC_LITTLE_ENDIAN);
                            add_cstring(ptv, &hf_wow_name);
                            add_cstring(ptv, &hf_wow_address);
                            ptvcursor_add(ptv, hf_wow_population, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_number_of_characters_on_realm, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_category, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_id, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_pop_subtree(ptv);
                        }
                        ptvcursor_add(ptv, hf_wow_footer_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_padding, 4, ENC_LITTLE_ENDIAN);
                    }
                break;
                case 8:
                    if (WOW_SERVER_TO_CLIENT) {
                        ptvcursor_add(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN);
                        ptvcursor_add(ptv, hf_wow_header_padding, 4, ENC_LITTLE_ENDIAN);
                        ptvcursor_add_ret_uint(ptv, hf_wow_number_of_realms, 2, ENC_LITTLE_ENDIAN, &number_of_realms);
                        for (uint32_t i1 = 0; i1 < number_of_realms; ++i1) {
                            ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Realm %i", i1);
                            ptvcursor_add(ptv, hf_wow_realm_type, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_locked, 1, ENC_NA);
                            ptvcursor_add_ret_uint(ptv, hf_wow_realm_flag, 1, ENC_LITTLE_ENDIAN, &flag);
                            add_cstring(ptv, &hf_wow_name);
                            add_cstring(ptv, &hf_wow_address);
                            ptvcursor_add(ptv, hf_wow_population, 4, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_number_of_characters_on_realm, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_category, 1, ENC_LITTLE_ENDIAN);
                            ptvcursor_add(ptv, hf_wow_realm_id, 1, ENC_LITTLE_ENDIAN);
                            if (flag & REALM_FLAG_SPECIFY_BUILD) {
                                ptvcursor_add_text_with_subtree(ptv, SUBTREE_UNDEFINED_LENGTH, ett_message, "Version");
                                ptvcursor_add(ptv, hf_wow_major, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_minor, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_patch, 1, ENC_LITTLE_ENDIAN);
                                ptvcursor_add(ptv, hf_wow_build, 2, ENC_LITTLE_ENDIAN);
                                ptvcursor_pop_subtree(ptv);
                            }
                            ptvcursor_pop_subtree(ptv);
                        }
                        ptvcursor_add(ptv, hf_wow_footer_padding, 2, ENC_LITTLE_ENDIAN);
                    }
                    else {
                        ptvcursor_add(ptv, hf_wow_padding, 4, ENC_LITTLE_ENDIAN);
                    }
                break;
            }
            break;
        case CMD_SURVEY_RESULT:
            ptvcursor_add(ptv, hf_wow_survey_id, 4, ENC_LITTLE_ENDIAN);
            ptvcursor_add(ptv, hf_wow_error, 1, ENC_LITTLE_ENDIAN);
            ptvcursor_add_ret_uint(ptv, hf_wow_compressed_data_length, 2, ENC_LITTLE_ENDIAN, &compressed_data_length);
            ptvcursor_add(ptv, hf_wow_data, compressed_data_length, ENC_NA);
            break;
        case CMD_XFER_DATA:
            ptvcursor_add_ret_uint(ptv, hf_wow_size, 2, ENC_LITTLE_ENDIAN, &size);
            ptvcursor_add(ptv, hf_wow_data, size, ENC_NA);
            break;
        case CMD_XFER_INITIATE:
            add_string(ptv, &hf_wow_filename);
            ptvcursor_add(ptv, hf_wow_file_size, 8, ENC_LITTLE_ENDIAN);
            ptvcursor_add(ptv, hf_wow_file_md, 16, ENC_NA);
            break;
        case CMD_XFER_RESUME:
            ptvcursor_add(ptv, hf_wow_offset, 8, ENC_LITTLE_ENDIAN);
            break;

        default:
            break;
    }
/* AUTOGENERATED_END_PARSER */
}

static int
dissect_wow_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	col_set_str(pinfo->cinfo, COL_PROTOCOL, "WOW");
	col_clear(pinfo->cinfo, COL_INFO);

	uint32_t offset = 0;
	uint8_t header_opcode = tvb_get_uint8(tvb, offset);

	col_set_str(pinfo->cinfo, COL_INFO,
			    val_to_str_const(header_opcode, cmd_vs,
				       "Unrecognized packet type"));

	if(!tree) {
		return tvb_captured_length(tvb);
	}

	proto_item* ti = proto_tree_add_item(tree, proto_wow, tvb, 0, -1, ENC_NA);
	proto_tree* wow_tree = proto_item_add_subtree(ti, ett_wow);

	ptvcursor_t* ptv = ptvcursor_new(wmem_packet_scope(), wow_tree, tvb, offset);

	ptvcursor_add(ptv, hf_wow_command, 1, ENC_LITTLE_ENDIAN);

        conversation_t* conv = find_or_create_conversation(pinfo);
        uint32_t* protocol_version = (uint32_t*)conversation_get_proto_data(conv, proto_wow);
        if (protocol_version == NULL) {
            protocol_version = (uint32_t*)wmem_new0(wmem_file_scope(), uint32_t);
            conversation_add_proto_data(conv, proto_wow, protocol_version);
            // 2 is the lowest valid version.
            *protocol_version = 2;
        }

        add_body_fields(pinfo, header_opcode, ptv, protocol_version);

        return tvb_captured_length(tvb);
}


static int
dissect_wow(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
	int8_t size_field_offset = -1;
	uint8_t cmd;

	cmd = tvb_get_uint8(tvb, 0);

	if(WOW_SERVER_TO_CLIENT && cmd == CMD_REALM_LIST)
		size_field_offset = 1;
	if(WOW_CLIENT_TO_SERVER && cmd == CMD_AUTH_LOGON_CHALLENGE)
		size_field_offset = 2;

	if(size_field_offset > -1) {
		tcp_dissect_pdus(tvb, pinfo, tree, wow_preference_desegment,
				 size_field_offset+2, get_wow_pdu_len,
				 dissect_wow_pdu, data);

	} else {
		/* Doesn't have a size field, so it cannot span multiple
		   segments.  Therefore, dissect this packet normally. */
		return dissect_wow_pdu(tvb, pinfo, tree, data);
	}

	return 0;
}


void
proto_register_wow(void)
{
	module_t *wow_module; /* For our preferences */

	static hf_register_info hf[] = {
        { &hf_wow_command,
          { "Command", "wow.cmd",
            FT_UINT8, BASE_HEX, VALS(cmd_vs), 0,
            "Type of packet", HFILL }
        },
        { &hf_wow_string_length,
            { "String Length", "wow.string.length",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                "Length of following string", HFILL }
        },
        /* AUTOGENERATED_START_REGISTER */
        { &hf_wow_account_flag,
            { "Account Flag", "wow.account.flag",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_account_name,
            { "Account Name", "wow.account.name",
                FT_STRINGZ, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_address,
            { "Address", "wow.address",
                FT_STRINGZ, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_authenticator,
            { "Authenticator", "wow.authenticator",
                FT_STRINGZ, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_build,
            { "Build", "wow.build",
                FT_UINT16, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_cd_key_proof,
            { "Cd Key Proof", "wow.cd.key.proof",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_challenge_count,
            { "Challenge Count", "wow.challenge.count",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_challenge_data,
            { "Challenge Data", "wow.challenge.data",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_checksum_salt,
            { "Checksum Salt", "wow.checksum.salt",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_client_checksum,
            { "Client Checksum", "wow.client.checksum",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_client_ip_address,
            { "Client Ip Address", "wow.client.ip.address",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_client_proof,
            { "Client Proof", "wow.client.proof",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_client_public_key,
            { "Client Public Key", "wow.client.public.key",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_compressed_data_length,
            { "Compressed Data Length", "wow.compressed.data.length",
                FT_UINT16, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_crc_hash,
            { "Crc Hash", "wow.crc.hash",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_crc_salt,
            { "Crc Salt", "wow.crc.salt",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_data,
            { "Data", "wow.data",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_decompressed_size,
            { "Decompressed Size", "wow.decompressed.size",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_digit_count,
            { "Digit Count", "wow.digit.count",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_error,
            { "Error", "wow.error",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_file_md,
            { "File Md", "wow.file.md",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_file_size,
            { "File Size", "wow.file.size",
                FT_UINT64, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_filename,
            { "Filename", "wow.filename",
                FT_STRINGZ, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_footer_padding,
            { "Footer Padding", "wow.footer.padding",
                FT_UINT16, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_game_name,
            { "Game Name", "wow.game.name",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_generator,
            { "Generator", "wow.generator",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_generator_length,
            { "Generator Length", "wow.generator.length",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_hardware_survey_id,
            { "Hardware Survey Id", "wow.hardware.survey.id",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_header_padding,
            { "Header Padding", "wow.header.padding",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_height,
            { "Height", "wow.height",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_key_count,
            { "Key Count", "wow.key.count",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_large_safe_prime,
            { "Large Safe Prime", "wow.large.safe.prime",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_large_safe_prime_length,
            { "Large Safe Prime Length", "wow.large.safe.prime.length",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_locale,
            { "Locale", "wow.locale",
                FT_UINT32, BASE_HEX_DEC, VALS(e_locale_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_locked,
            { "Locked", "wow.locked",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_login_result,
            { "Login Result", "wow.login.result",
                FT_UINT8, BASE_HEX_DEC, VALS(e_login_result_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_major,
            { "Major", "wow.major",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_matrix_card_proof,
            { "Matrix Card Proof", "wow.matrix.card.proof",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_minor,
            { "Minor", "wow.minor",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_name,
            { "Name", "wow.name",
                FT_STRINGZ, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_number_of_characters_on_realm,
            { "Number Of Characters On Realm", "wow.number.of.characters.on.realm",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_number_of_realms,
            { "Number Of Realms", "wow.number.of.realms",
                FT_UINT16, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_number_of_telemetry_keys,
            { "Number Of Telemetry Keys", "wow.number.of.telemetry.keys",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_offset,
            { "Offset", "wow.offset",
                FT_UINT64, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_os,
            { "Os", "wow.os",
                FT_UINT32, BASE_HEX_DEC, VALS(e_os_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_padding,
            { "Padding", "wow.padding",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_patch,
            { "Patch", "wow.patch",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_pin_grid_seed,
            { "Pin Grid Seed", "wow.pin.grid.seed",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_pin_hash,
            { "Pin Hash", "wow.pin.hash",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_pin_salt,
            { "Pin Salt", "wow.pin.salt",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_platform,
            { "Platform", "wow.platform",
                FT_UINT32, BASE_HEX_DEC, VALS(e_platform_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_population,
            { "Population", "wow.population",
                FT_FLOAT, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_proof_data,
            { "Proof Data", "wow.proof.data",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_protocol_version,
            { "Protocol Version", "wow.protocol.version",
                FT_UINT8, BASE_HEX_DEC, VALS(e_protocol_version_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_protocol_version_int,
            { "Protocol Version Int", "wow.protocol.version.int",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_realm_category,
            { "Realm Category", "wow.realm.category",
                FT_UINT8, BASE_HEX_DEC, VALS(e_realm_category_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_realm_flag,
            { "Realm Flag", "wow.realm.flag",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_realm_id,
            { "Realm Id", "wow.realm.id",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_realm_type,
            { "Realm Type", "wow.realm.type",
                FT_UINT8, BASE_HEX_DEC, VALS(e_realm_type_strings), 0,
                NULL, HFILL
            }
        },
        { &hf_wow_required,
            { "Required", "wow.required",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_salt,
            { "Salt", "wow.salt",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_security_flag,
            { "Security Flag", "wow.security.flag",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_seed,
            { "Seed", "wow.seed",
                FT_UINT64, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_server_proof,
            { "Server Proof", "wow.server.proof",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_server_public_key,
            { "Server Public Key", "wow.server.public.key",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_size,
            { "Size", "wow.size",
                FT_UINT16, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_survey_id,
            { "Survey Id", "wow.survey.id",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_unknown_bytes,
            { "Unknown Bytes", "wow.unknown.bytes",
                FT_BYTES, BASE_NONE, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_unknown_int,
            { "Unknown Int", "wow.unknown.int",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_utc_timezone_offset,
            { "Utc Timezone Offset", "wow.utc.timezone.offset",
                FT_UINT32, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
        { &hf_wow_width,
            { "Width", "wow.width",
                FT_UINT8, BASE_HEX_DEC, NULL, 0,
                NULL, HFILL
            }
        },
/* AUTOGENERATED_END_REGISTER */
	};

	static int *ett[] = {
		&ett_wow,
        &ett_message,
	};

	proto_wow = proto_register_protocol("World of Warcraft",
					    "WOW", "wow");

	proto_register_field_array(proto_wow, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	wow_handle = register_dissector("wow", dissect_wow, proto_wow);

	wow_module = prefs_register_protocol(proto_wow, NULL);

	prefs_register_bool_preference(wow_module, "desegment", "Reassemble wow messages spanning multiple TCP segments.", "Whether the wow dissector should reassemble messages spanning multiple TCP segments.  To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", &wow_preference_desegment);

}

void
proto_reg_handoff_wow(void)
{
	dissector_add_uint_with_preference("tcp.port", WOW_PORT, wow_handle);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */
